package self.zhangkang.chart.view.kline.main

import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Path
import self.zhangkang.chart.data.KLineInfo
import self.zhangkang.chart.tools.CanvasTools
import self.zhangkang.chart.tools.DataTools
import self.zhangkang.chart.tools.DecimalFormatTools
import self.zhangkang.chart.view.IndexNameConfig
import self.zhangkang.chart.view.kline.KLineIndex
import self.zhangkang.chart.view.kline.KLineIndexInfo
import self.zhangkang.chart.view.kline.KLineViewColor
import self.zhangkang.chart.view.kline.KLineViewDimen
import self.zhangkang.chart.view.pool.PathPool
import self.zhangkang.chart.view.pool.RectPool

/**
 * @author : Android-张康
 * created on: 2023/5/9 18:19
 * description: 基础的K线，仅包含蜡烛图
 */
internal open class BaseMainKLine(
    private val pathPool: PathPool,
    private val rectPool: RectPool,
    private val kLineViewColor: KLineViewColor,
    private val kLineViewDimen: KLineViewDimen,
    private val indexNameConfig: IndexNameConfig
) : KLineIndexInfo {

    companion object {
        private const val LINE_TOP_BOTTOM_SPACE = 0.005F
    }

    /**
     * 记录最大值
     */
    private var mMaxValue: Float = 0F

    /**
     * 记录最小值
     */
    private var mMinValue: Float = 0F

    /**
     * 记录最大的值
     */
    private var mMaxWithinRange: KLineInfo? = null

    /**
     * 记录最小的值
     */
    private var mMinWithinRange: KLineInfo? = null

    /**
     * 每个价格对应的高度
     */
    private var mPriceRangeItem: Float = 0.0F

    /**
     * K线的路径
     */
    private var kLinePath: Path? = null

    /**
     * 控件的一半。用来区分绘制最大值和最小值时的左右位置
     */
    private var halfWidth: Float = 0.0F

    /**
     * 是否可以切换
     *
     * @return true 可以切换，false 不可以切换
     */
    override fun canSwitch(): Boolean {
        return getIndexInfo().canSwitch
    }

    /**
     * 是否点击到名称
     *
     * @param startX 开始X
     * @param startY 开始Y
     * @param stopX 结束X
     * @param stopY 结束Y
     * @param touchX 触摸的 X
     * @param touchY 触摸的 Y
     * @param textPaint 文字的画笔
     *
     * @return true 是，false 否
     */
    override fun isClickInName(
        startX: Float,
        startY: Float,
        stopX: Float,
        stopY: Float,
        touchX: Float,
        touchY: Float,
        textPaint: Paint
    ): Boolean {
        val textBoundsRect = rectPool.acquire()
        //绘制指标名称
        CanvasTools.getDrawTextBounds(
            startX = startX,
            startY = startY,
            stopY = stopY,
            textPaint = textPaint,
            indicatorName = getIndexInfo().indexName,
            indexNameConfig = indexNameConfig,
            textBoundsRect = textBoundsRect,
            hasArrowDropDown = canSwitch(),
        )
        val contains = textBoundsRect.contains(touchX.toInt(), touchY.toInt())
        rectPool.release(textBoundsRect)
        return contains
    }

    /**
     * 获取指标类型
     */
    override fun getIndexInfo(): KLineIndex {
        return KLineIndex.UNKNOWN
    }

    /**
     * 获取占的比例
     */
    override fun getProportion(): Int {
        return 55
    }

    /**
     * 计算最大值前
     *
     * @param firstKLineInfo 第一个值
     */
    override fun calculationMaxAndMinBefore(firstKLineInfo: KLineInfo) {
        mMaxValue = firstKLineInfo.lowerPrice
        mMinValue = firstKLineInfo.highPrice
        mMaxWithinRange = firstKLineInfo
        mMinWithinRange = firstKLineInfo
    }

    /**
     * 计算最大值
     *
     * @param kLineInfo 当前值
     */
    override fun calculationMaxAndMin(kLineInfo: KLineInfo) {
        if (kLineInfo.highPrice > mMaxValue) {
            mMaxValue = kLineInfo.highPrice
            mMaxWithinRange = kLineInfo
        }
        if (kLineInfo.lowerPrice < mMinValue) {
            mMinValue = kLineInfo.lowerPrice
        }
        if (kLineInfo.lowerPrice < (mMinWithinRange?.lowerPrice ?: Float.MIN_VALUE)) {
            mMinWithinRange = kLineInfo
        }
    }

    /**
     * 计算最大值后
     */
    override fun calculationMaxAndMinAfter() {
        val openPrice = mMinValue + (mMaxValue - mMinValue) / 2
        mMaxValue += openPrice * LINE_TOP_BOTTOM_SPACE
        mMinValue -= openPrice * LINE_TOP_BOTTOM_SPACE
        if (mMinValue < 0) {
            mMinValue = 0F
            mMaxValue = openPrice + (openPrice - mMinValue)
        }
        mMaxValue = mMaxValue.coerceAtLeast(openPrice * (1 + LINE_TOP_BOTTOM_SPACE))
        mMinValue = mMinValue.coerceAtMost(openPrice * (1 - LINE_TOP_BOTTOM_SPACE))
    }

    /**
     * 绘制背景
     *
     * @param canvas 画布
     * @param startX 开始X
     * @param startY 开始Y
     * @param stopX 结束X
     * @param stopY 结束Y
     * @param linePaint 线画笔
     * @param textPaint 文字画笔
     */
    override fun drawBackground(
        canvas: Canvas,
        startX: Float,
        startY: Float,
        stopX: Float,
        stopY: Float,
        linePaint: Paint,
        textPaint: Paint
    ) {
        linePaint.strokeWidth = kLineViewDimen.borderLineStrokeWidth
        linePaint.color = kLineViewColor.borderLineLineColor
        linePaint.style = Paint.Style.STROKE
        //绘制边框的线
        val backgroundPath = pathPool.acquire()
        canvas.drawPath(backgroundPath.apply {
            reset()
            moveTo(startX, startY)
            lineTo(stopX, startY)
            lineTo(stopX, stopY)
            lineTo(startX, stopY)
            lineTo(startX, startY)
        }, linePaint)
        //绘制横线同时绘制纵坐标
        //每个分段对应的高
        val itemHeight = (stopY - startY) / 4
        val itemPrice = (mMaxValue - mMinValue) / 4
        val textStartX = startX + kLineViewDimen.textPaddingLine
        textPaint.apply {
            color = kLineViewColor.ordinateTextColor
            textSize = kLineViewDimen.ordinateTextSize
        }
        repeat(5) {
            val itemStopY = startY + itemHeight * it
            when (it) {
                0 -> {
                    val leftText = DecimalFormatTools.retainTwoDecimalPlaces(mMaxValue)
                    val textBoundsRect = rectPool.acquire()
                    textPaint.getTextBounds(leftText, 0, leftText.length, textBoundsRect)
                    textPaint.color = kLineViewColor.redColor
                    val textY =
                        itemStopY + textBoundsRect.height() + kLineViewDimen.textPaddingLine
                    canvas.drawText(
                        leftText,
                        textStartX,
                        textY,
                        textPaint
                    )
                    rectPool.release(textBoundsRect)
                }

                4 -> {
                    val leftText = DecimalFormatTools.retainTwoDecimalPlaces(mMinValue)
                    textPaint.color = kLineViewColor.greenColor
                    val textY = itemStopY - kLineViewDimen.textPaddingLine

                    canvas.drawText(
                        leftText,
                        textStartX,
                        textY,
                        textPaint
                    )
                }

                else -> {
                    val linePath = pathPool.acquire()
                    linePath.moveTo(startX, itemStopY)
                    linePath.lineTo(stopX, itemStopY)
                    linePaint.color = kLineViewColor.dashLineColor
                    linePaint.pathEffect = kLineViewDimen.dashPathEffect
                    canvas.drawPath(linePath, linePaint)
                    linePaint.pathEffect = null
                    pathPool.release(linePath)
                }
            }
        }
        pathPool.release(backgroundPath)
    }

    /**
     * 绘制当前值
     *
     * @param canvas 画布
     * @param startX 开始的 X
     * @param startY 开的的 Y
     * @param stopX 结束的 X
     * @param stopY 结束的 Y
     * @param linePaint 线的画笔
     * @param textPaint 文字的画笔
     * @param marginTop 距离顶部的距离
     * @param span
     * @param indexValueHorizontalSpace 指标值之间的横向距离
     * @param indexValueVerticalSpace 指标值之间的纵向距离
     * @param kLineInfo 当前的值
     * @param kLineIsLine K线是否为一条线
     *
     */
    final override fun drawCurrentIndexInfo(
        canvas: Canvas,
        startX: Float,
        startY: Float,
        stopX: Float,
        stopY: Float,
        linePaint: Paint,
        textPaint: Paint,
        marginTop: Float,
        span: Int,
        indexValueHorizontalSpace: Float,
        indexValueVerticalSpace: Float,
        kLineInfo: KLineInfo,
        kLineIsLine: Boolean
    ) {
        drawMainCurrentIndexInfo(
            canvas,
            startX,
            startY,
            stopX,
            stopY,
            linePaint,
            textPaint,
            marginTop,
            span,
            indexValueHorizontalSpace,
            indexValueVerticalSpace,
            kLineInfo,
            kLineIsLine
        )
    }

    /**
     * 绘制当前值内容之前
     *
     * @param canvas 画布
     * @param startX 开始的 X
     * @param startY 开的的 Y
     * @param stopX 结束的 X
     * @param stopY 结束的 Y
     * @param paint 画笔
     * @param candlestickWidth 蜡烛的宽度
     * @param candlestickWidthSpace 蜡烛之间的距离
     * @param kLineInfo 当前的值
     * @param kLineIsLine K线是否为一条线
     */
    override fun beforeDrawItemContent(
        canvas: Canvas,
        startX: Float,
        startY: Float,
        stopX: Float,
        stopY: Float,
        paint: Paint,
        candlestickWidth: Float,
        candlestickWidthSpace: Float,
        kLineInfo: KLineInfo,
        kLineIsLine: Boolean
    ) {
        val valueRange = mMaxValue - mMinValue

        val drawHeight = stopY - startY

        mPriceRangeItem = drawHeight / valueRange

        halfWidth = (stopX - startX) / 2
        if (kLineIsLine) {
            kLinePath = pathPool.acquire()
        }
    }

    /**
     * 绘制当前值内容
     *
     * @param canvas 画布
     * @param startX 开始的 X
     * @param startY 开的的 Y
     * @param stopX 结束的 X
     * @param stopY 结束的 Y
     * @param textPaint 文字的画笔
     * @param linePaint 线的画笔
     * @param kLineInfo 当前的值
     * @param isFirst 是否为第一次
     * @param yangLineIsFill 阳线是否实心
     * @param totalTranslateX X轴总的位移
     * @param candlestickWidth 蜡烛的宽度
     * @param halfCandlestickWidth 蜡烛的一半宽度
     * @param kLineIsLine K线是否为一条线
     */
    override fun drawItemContent(
        canvas: Canvas,
        startX: Float,
        startY: Float,
        stopX: Float,
        stopY: Float,
        textPaint: Paint,
        linePaint: Paint,
        kLineInfo: KLineInfo,
        isFirst: Boolean,
        yangLineIsFill: Boolean,
        totalTranslateX: Float,
        candlestickWidth: Float,
        halfCandlestickWidth: Float,
        kLineIsLine: Boolean
    ) {
        //最高价格的 Y 坐标
        val highPriceY = mPriceRangeItem * (mMaxValue - kLineInfo.highPrice) + startY
        //开盘价格的 Y 坐标
        val openPriceY = mPriceRangeItem * (mMaxValue - kLineInfo.openPrice) + startY
        //收盘价格的 Y 坐标
        val closePriceY = mPriceRangeItem * (mMaxValue - kLineInfo.closePrice) + startY
        //最低价格的 Y 坐标
        val lowerPriceY = mPriceRangeItem * (mMaxValue - kLineInfo.lowerPrice) + startY
        //Path 的横坐标
        var pathX = halfCandlestickWidth + totalTranslateX
        if (pathX < 0) {
            pathX = halfCandlestickWidth
        }
        if (isFirst) {
            kLinePath?.moveTo(pathX, closePriceY)
        } else {
            kLinePath?.lineTo(pathX, closePriceY)
        }
        if (!kLineIsLine) {
            //绘制蜡烛图
            drawCandlestick(
                canvas,
                kLineInfo,
                highPriceY,
                lowerPriceY,
                closePriceY,
                openPriceY,
                candlestickWidth = candlestickWidth,
                linePaint = linePaint,
                yangLineIsFill = yangLineIsFill
            )
        }
        //绘制最高价格和最低价格
        drawMaxOrMinPrice(
            canvas,
            kLineInfo,
            totalTranslateX,
            halfWidth,
            highPriceY,
            lowerPriceY,
            mCandlestickWidth = candlestickWidth,
            textPaint = textPaint
        )
        //绘制除复权标识
        if (kLineInfo.adjustFlag) {
            textPaint.color = kLineViewColor.adjustColor
            textPaint.textSize = kLineViewDimen.adjustTextSize
            textPaint.isUnderlineText = true
            val mTextBoundsRect = rectPool.acquire()
            textPaint.getTextBounds("S", 0, 1, mTextBoundsRect)
            val textStartX = (candlestickWidth - mTextBoundsRect.width()) / 2
            canvas.drawText(
                "S",
                textStartX,
                stopY - kLineViewDimen.maxOrMinPriceTextPaddingLine,
                textPaint
            )
            textPaint.isUnderlineText = false
            rectPool.release(mTextBoundsRect)
        }
    }

    /**
     * 绘制完整个当前内容之后
     *
     * @param canvas 画布
     * @param paint 画笔
     */
    override fun afterDrawItemContent(canvas: Canvas, paint: Paint) {
        paint.strokeWidth = kLineViewDimen.indexLineStrokeWidth
        kLinePath?.let {
            paint.color = kLineViewColor.kLineIsLineColor
            canvas.drawPath(it, paint)
            pathPool.release(it)
            kLinePath = null
        }
    }

    /**
     * 获取当前左边的值
     *
     * @param percentage 比例
     *
     * @return 返回当前左边坐标的值
     */
    override fun getCurrentLeftValue(percentage: Float): String? {
        return null
    }

    /**
     * 获取当前右边的值
     *
     * @param percentage 比例
     *
     * @return 返回当前右边坐标的值
     */
    override fun getCurrentRightValue(percentage: Float): String? {
        return null
    }

    /**
     * 绘制当前值。
     *
     * @param canvas 画布
     * @param startX 开始的 X
     * @param startY 开的的 Y
     * @param stopX 结束的 X
     * @param stopY 结束的 Y
     * @param linePaint 线的画笔
     * @param textPaint 文字的画笔
     * @param marginTop 距离顶部的距离
     * @param span
     * @param indexValueHorizontalSpace 指标值之间的横向距离
     * @param indexValueVerticalSpace 指标值之间的纵向距离
     * @param kLineInfo 当前的值
     * @param kLineIsLine K线是否为一条线
     *
     * @return 绘制结束 X 坐标
     */
    open fun drawMainCurrentIndexInfo(
        canvas: Canvas,
        startX: Float,
        startY: Float,
        stopX: Float,
        stopY: Float,
        linePaint: Paint,
        textPaint: Paint,
        marginTop: Float,
        span: Int,
        indexValueHorizontalSpace: Float,
        indexValueVerticalSpace: Float,
        kLineInfo: KLineInfo,
        kLineIsLine: Boolean
    ): Float {
        //绘制指标名称
        return CanvasTools.drawIndicatorName(
            canvas = canvas,
            startX = startX,
            startY = startY,
            stopY = stopY,
            linePaint = linePaint,
            textPaint = textPaint,
            rectPool = rectPool,
            indicatorName = getIndexInfo().indexName,
            indexNameConfig = indexNameConfig,
            hasArrowDropDown = canSwitch(),
        )
    }

    /**
     * 获取最大值
     */
    protected fun getMaxValue() = mMaxValue

    /**
     * 获取最小值
     */
    protected fun getMinValue() = mMinValue

    /**
     * 获取每个价格对应的高度
     */
    protected fun getPriceRangeItem() = mPriceRangeItem

    /**
     * 设置最大值和最小值
     */
    protected fun setMaxAndMinValue(maxValue: Float, minValue: Float) {
        mMaxValue = maxValue
        mMinValue = minValue
    }

    /**
     * 绘制蜡烛图
     */
    private fun drawCandlestick(
        canvas: Canvas,
        kLineInfo: KLineInfo,
        highPriceY: Float,
        lowerPriceY: Float,
        closePriceY: Float,
        openPriceY: Float,
        candlestickWidth: Float,
        linePaint: Paint,
        yangLineIsFill: Boolean
    ) {
        //判断当前绘制的颜色
        val candlestickColor = if (DataTools.kLineInfoIsUp(kLineInfo)) {
            if (!yangLineIsFill) {
                linePaint.style = Paint.Style.STROKE
            }
            kLineViewColor.redColor
        } else {
            kLineViewColor.greenColor
        }

        linePaint.color = candlestickColor
        //竖线的中间位置
        val middlePositionOfVerticalLine = candlestickWidth / 2
        //绘制竖线
        if (kLineInfo.closePrice <= kLineInfo.openPrice || yangLineIsFill) {
            linePaint.style = Paint.Style.FILL_AND_STROKE
            canvas.drawLine(
                middlePositionOfVerticalLine,
                highPriceY,
                middlePositionOfVerticalLine,
                lowerPriceY,
                linePaint
            )
        } else {
            linePaint.style = Paint.Style.STROKE
            //此处收盘价大于开盘价，先绘制最高价到收盘价之间的线，即上影线
            canvas.drawLine(
                middlePositionOfVerticalLine,
                highPriceY,
                middlePositionOfVerticalLine,
                closePriceY,
                linePaint
            )
            //再绘制开盘价到最低价之间的线，即下影线
            canvas.drawLine(
                middlePositionOfVerticalLine,
                openPriceY,
                middlePositionOfVerticalLine,
                lowerPriceY,
                linePaint
            )
        }
        //绘制中间方框
        if (kLineInfo.openPrice == kLineInfo.closePrice) {
            canvas.drawLine(0F, openPriceY, candlestickWidth, closePriceY, linePaint)
        } else {
            canvas.drawRect(0F, openPriceY, candlestickWidth, closePriceY, linePaint)
        }
    }

    /**
     * 绘制最高价和最低价
     */
    private fun drawMaxOrMinPrice(
        canvas: Canvas,
        kLineInfo: KLineInfo,
        totalTranslateX: Float,
        halfWidth: Float,
        highPriceY: Float,
        lowerPriceY: Float,
        mCandlestickWidth: Float,
        textPaint: Paint
    ) {
        if (mMinWithinRange == kLineInfo || mMaxWithinRange == kLineInfo) {
            textPaint.color = kLineViewColor.maxOrMinPriceTextColor
            textPaint.textSize = kLineViewDimen.ordinateTextSize
            if (mMaxWithinRange == kLineInfo) {
                val drawText: String = if (totalTranslateX > halfWidth) {
                    "${kLineInfo.highPrice} →"
                } else {
                    "← ${kLineInfo.highPrice}"
                }
                val textBoundsRect = rectPool.acquire()
                textPaint.getTextBounds(drawText, 0, drawText.length, textBoundsRect)
                val textY =
                    highPriceY + textBoundsRect.height() + kLineViewDimen.maxOrMinPriceTextPaddingLine
                val textX = if (totalTranslateX > halfWidth) {
                    -textBoundsRect.width() - mCandlestickWidth
                } else {
                    mCandlestickWidth
                }
                canvas.drawText(drawText, textX, textY, textPaint)
                rectPool.release(textBoundsRect)
            } else {
                val drawText: String = if (totalTranslateX > halfWidth) {
                    "${kLineInfo.lowerPrice} →"
                } else {
                    "← ${kLineInfo.lowerPrice}"
                }
                val textY = lowerPriceY - kLineViewDimen.maxOrMinPriceTextPaddingLine
                val textBoundsRect = rectPool.acquire()
                val textX = if (totalTranslateX > halfWidth) {
                    textPaint.getTextBounds(drawText, 0, drawText.length, textBoundsRect)
                    -textBoundsRect.width() - 3.0F
                } else {
                    mCandlestickWidth
                }
                canvas.drawText(drawText, textX, textY, textPaint)
                rectPool.release(textBoundsRect)
            }
        }
    }
}